﻿using System.Collections.Generic;
using UnityEngine;
using System;
using System.Text.RegularExpressions;

namespace Mars.DeviceProfiles
{
    public class AndroidDeviceProfileSelector : IDeviceProfileSelector
    {
        // 配置文件相关
        private const string fileName = "AndroidCommonDeviceProfileConfig"; // json格式的存储配置文件名

        private FAndroidCommonDeviceProfileConfig config; // 解析后的json数据

        // 设备相关的信息
        private string GPUFamily;
        private string GLVersion;
        private string DeviceModel;
        private string DeviceMake;
        private string OSVersion;
        private string MemorySizeInGB;
        private string CPUNumCores;
        private string CPUMaxFreq;
        private string VulkanAvailable;
        private string VulkanVersion;
        private string UsingHoudini;

        public string GetGPUFamily()
        {
            return GPUFamily;
        }

        public string GetGLVersion()
        {
            return GLVersion;
        }

        public string GetDeviceModel()
        {
            return DeviceModel;
        }

        public string GetDeviceMake()
        {
            return DeviceMake;
        }

        public string GetOSVersion()
        {
            return OSVersion;
        }

        public string GetMemorySizeInGB()
        {
            return MemorySizeInGB;
        }

        public string GetCPUNumCores()
        {
            return CPUNumCores;
        }

        public string GetCPUMaxFreq()
        {
            return CPUMaxFreq;
        }

        public int GetScore()
        {
            return FindMatchingProfileScoreCommon(GPUFamily, GLVersion, MemorySizeInGB, CPUNumCores, CPUMaxFreq);
        }

        // 计算后的配置文件名 用于区分每一个细分类型
        public string Profile;

        // 文件文件名和设备分档的映射
        private Dictionary<string, string> ProfileGradeMap = new Dictionary<string, string>();

        void InitProfileGradeMapData()
        {
            ProfileGradeMap.Clear();
            if (config == null)
            {
                return;
            }

            for (int i = 0; i < config.GradeScoreProfileName.Length; i++)
            {
                ProfileGradeMap[config.GradeScoreProfileName[i].ProfileName] =
                    config.GradeScoreProfileName[i].TCQualityGrade;
            }

            for (int i = 0; i < config.GradeMatchProfileWhiteList.Length; i++)
            {
                ProfileGradeMap[config.GradeMatchProfileWhiteList[i].Profile] =
                    config.GradeMatchProfileWhiteList[i].TCQualityGrade;
            }
        }

        void InitDeviceInfo()
        {
            GPUFamily = SystemInfo.graphicsDeviceName;
            Debug.Log("GPUFamily: " + GPUFamily);
            GLVersion = SystemInfo.graphicsDeviceVersion;
            Debug.Log("GLVersion: " + GLVersion);
            string deviceModel = SystemInfo.deviceModel;
            int index = deviceModel.IndexOf(' ');
            DeviceModel = deviceModel.Substring(index + 1);
            Debug.Log("DeviceModel: " + DeviceModel);
            if (index > 0)
            {
                DeviceMake = deviceModel.Substring(0, index);
                Debug.Log("DeviceMake: " + DeviceMake);
            }

            OSVersion = SystemInfo.operatingSystem;
            string OSPrefixStr = "Android OS ";
            index = OSVersion.IndexOf(OSPrefixStr);
            if (index >= 0)
            {
                OSVersion = OSVersion.Substring(index + OSPrefixStr.Length);
            }

            Debug.Log("AndroidVersion: " + OSVersion);
            // 内存换成GB单位，并且上取整数
            int GBData = SystemInfo.systemMemorySize % 1024 == 0
                ? SystemInfo.systemMemorySize / 1024
                : SystemInfo.systemMemorySize / 1024 + 1;
            MemorySizeInGB = GBData.ToString();
            Debug.Log("MemorySizeInGB: " + MemorySizeInGB);
            CPUNumCores = SystemInfo.processorCount.ToString();
            Debug.Log("CPUNumCores: " + CPUNumCores);
            CPUMaxFreq = SystemInfo.processorFrequency.ToString();
            Debug.Log("CPUMaxFreq: " + CPUMaxFreq);
            // 这三个参数暂时没法获取
            VulkanAvailable = "";
            VulkanVersion = "";
            UsingHoudini = "";
        }

        // 初始化数据
        public void InitData()
        {
            LoadJsonFromFile();
            InitProfileGradeMapData();
            InitDeviceInfo();
        }

        private void LoadJsonFromFile()
        {
            TextAsset file = Resources.Load(fileName) as TextAsset;
            if (file != null)
            {
                string dataAsJson = file.ToString();
                config = JsonUtility.FromJson<FAndroidCommonDeviceProfileConfig>(dataAsJson);
                Debug.Log("AndroidCommonDeviceProfile config Data loaded!");
            }
            else
            {
                Debug.LogError(
                    "Cannot find AndroidCommonDeviceProfileConfig.json! Make sure file in \"Assets/StreamingAssets\" path. And file suffix should be \".json \"");
            }
        }

        private string FindMatchingProfile(FProfileMatch[] MatchProfile, string GPUFamily, string GLVersion,
            string AndroidVersion, string DeviceMake, string DeviceModel, string VulkanAvailable, string VulkanVersion,
            string UsingHoudini, string MemorySizeInGB, string CPUNumCores, string CPUMaxFreq)
        {
            string OutProfileName = "";
            for (int i = 0; i < MatchProfile.Length; i++)
            {
                FProfileMatch Profile = (FProfileMatch)MatchProfile[i];
                string PreviousRegexMatch = "";
                bool bFoundMatch = true;
                for (int j = 0; j < Profile.Match.Length; j++)
                {
                    FProfileMatchItem Item = (FProfileMatchItem)Profile.Match[j];
                    string SourceString;
                    if (!Enum.IsDefined(typeof(ESourceType), Item.SourceType))
                    {
                        bFoundMatch = false;
                        Debug.LogError("ESourceType config error : " + Item.SourceType);
                        break;
                    }

                    ESourceType SourceType = (ESourceType)Enum.Parse(typeof(ESourceType), Item.SourceType);
                    switch (SourceType)
                    {
                        case ESourceType.SRC_PreviousRegexMatch:
                            SourceString = PreviousRegexMatch;
                            break;
                        case ESourceType.SRC_GpuFamily:
                            SourceString = GPUFamily;
                            break;
                        case ESourceType.SRC_GlVersion:
                            SourceString = GLVersion;
                            break;
                        case ESourceType.SRC_AndroidVersion:
                            SourceString = AndroidVersion;
                            break;
                        case ESourceType.SRC_DeviceMake:
                            SourceString = DeviceMake;
                            break;
                        case ESourceType.SRC_DeviceModel:
                            SourceString = DeviceModel;
                            break;
                        case ESourceType.SRC_VulkanVersion:
                            SourceString = VulkanVersion;
                            break;
                        case ESourceType.SRC_UsingHoudini:
                            SourceString = UsingHoudini;
                            break;
                        case ESourceType.SRC_VulkanAvailable:
                            SourceString = VulkanAvailable;
                            break;
                        case ESourceType.SRC_MemorySizeInGB:
                            SourceString = MemorySizeInGB;
                            break;
                        default:
                            continue;
                    }

                    if (!Enum.IsDefined(typeof(ECompareType), Item.CompareType))
                    {
                        bFoundMatch = false;
                        Debug.LogError("ECompareType config error : " + Item.CompareType);
                        break;
                    }

                    ECompareType CompareType = (ECompareType)Enum.Parse(typeof(ECompareType), Item.CompareType);
                    switch (CompareType)
                    {
                        case ECompareType.CMP_Equal:
                            if (!SourceString.Equals(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_Less:
                            if (Int32.Parse(SourceString) >= Int32.Parse(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_LessEqual:
                            if (Int32.Parse(SourceString) > Int32.Parse(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_Greater:
                            if (Int32.Parse(SourceString) <= Int32.Parse(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_GreaterEqual:
                            if (Int32.Parse(SourceString) < Int32.Parse(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_NotEqual:
                            if (SourceString == Item.MatchString)
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_Regex:
                        {
                            Regex reg = new Regex(Item.MatchString);
                            if (reg.IsMatch(SourceString))
                            {
                                GroupCollection groups = reg.Match(SourceString).Groups;
                                PreviousRegexMatch = groups[1].ToString();
                            }
                            else
                            {
                                bFoundMatch = false;
                            }
                        }
                            break;
                        default:
                            bFoundMatch = false;
                            break;
                    }

                    if (!bFoundMatch)
                    {
                        break;
                    }
                }

                if (bFoundMatch)
                {
                    OutProfileName = Profile.Profile;
                    break;
                }
            }

            return OutProfileName;
        }

        private int FindMatchingProfileScoreCommon(string GPUFamily, string GLVersion, string MemorySizeInGB,
            string CPUNumCores, string CPUMaxFreq)
        {
            string OutProfileName = "";
            int[] scoreList = new int[(int)EGradeScoreType.GST_MAX];
            for (int i = 0; i < scoreList.Length; i++)
            {
                scoreList[i] = 0;
            }

            for (int i = 0; i < config.GradeMatchProfile.Length; i++)
            {
                bool bFoundMatch = true;
                FGradeProfileMatch Profile = config.GradeMatchProfile[i];

                for (int j = 0; j < Profile.Match.Length; j++)
                {
                    FProfileMatchItem Item = Profile.Match[j];
                    string SourceString = "";
                    if (!Enum.IsDefined(typeof(ESourceType), Item.SourceType))
                    {
                        bFoundMatch = false;
                        Debug.LogError("ESourceType config error : " + Item.SourceType);
                        break;
                    }

                    ESourceType SourceType = (ESourceType)Enum.Parse(typeof(ESourceType), Item.SourceType);
                    switch (SourceType)
                    {
                        case ESourceType.SRC_MemorySizeInGB:
                            SourceString = MemorySizeInGB;
                            break;
                        case ESourceType.SRC_CPUCoreNum:
                            SourceString = CPUNumCores;
                            break;
                        case ESourceType.SRC_CPUMaxFreq:
                            SourceString = CPUMaxFreq;
                            break;
                        case ESourceType.SRC_GpuFamily:
                            SourceString = GPUFamily;
                            break;
                        case ESourceType.SRC_GlVersion:
                            SourceString = GLVersion;
                            break;
                        default:
                            continue;
                    }

                    if (!Enum.IsDefined(typeof(ECompareType), Item.CompareType))
                    {
                        bFoundMatch = false;
                        Debug.LogError("ECompareType config error : " + Item.CompareType);
                        break;
                    }

                    ECompareType CompareType = (ECompareType)Enum.Parse(typeof(ECompareType), Item.CompareType);
                    switch (CompareType)
                    {
                        case ECompareType.CMP_Equal:
                            if (!SourceString.Equals(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_Less:
                            if (Int32.Parse(SourceString) >= Int32.Parse(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_LessEqual:
                            if (Int32.Parse(SourceString) > Int32.Parse(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_Greater:
                            if (Int32.Parse(SourceString) <= Int32.Parse(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_GreaterEqual:
                            if (Int32.Parse(SourceString) < Int32.Parse(Item.MatchString))
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_NotEqual:
                            if (SourceString == Item.MatchString)
                            {
                                bFoundMatch = false;
                            }

                            break;
                        case ECompareType.CMP_Regex:
                        {
                            Regex reg = new Regex(Item.MatchString);
                            if (reg.IsMatch(SourceString))
                            {
                                GroupCollection groups = reg.Match(SourceString).Groups;
                            }
                            else
                            {
                                bFoundMatch = false;
                            }
                        }
                            break;
                        default:
                            bFoundMatch = false;
                            break;
                    }

                    if (!bFoundMatch)
                    {
                        break;
                    }
                }

                if (bFoundMatch)
                {
                    if (Enum.IsDefined(typeof(EGradeScoreType), Profile.ScoreType))
                    {
                        EGradeScoreType ScoreSourceType =
                            (EGradeScoreType)Enum.Parse(typeof(EGradeScoreType), Profile.ScoreType);
                        scoreList[(int)ScoreSourceType] = Profile.Score;
                    }
                    else
                    {
                        Debug.LogError("EGradeScoreType config error : " + Profile.ScoreType);
                    }
                }
            }

            for (int i = 0; i < (int)EGradeScoreType.GST_MAX; i++)
            {
                if ((int)scoreList[i] <= 0)
                {
                    scoreList[i] = config.MaxScore / 2;
                }
            }

            float sumValue = 0;
            for (int i = 0; i < (int)EGradeScoreType.GST_MAX; i++)
            {
                sumValue += (float)((float)(config.GradeScoreTypePercentage[i].value) * (float)scoreList[i]);
            }

            return (int)sumValue;
        }


        private string FindMatchingProfileCommon(string GPUFamily, string GLVersion, string AndroidVersion,
            string DeviceMake, string DeviceModel, string VulkanAvailable, string VulkanVersion, string UsingHoudini,
            string MemorySizeInGB, string CPUNumCores, string CPUMaxFreq)
        {
            string OutProfileName = "";

            float sumValue =
                FindMatchingProfileScoreCommon(GPUFamily, GLVersion, MemorySizeInGB, CPUNumCores, CPUMaxFreq);

            // 这里最好排序一下
            for (int i = config.GradeScoreProfileName.Length - 1; i >= 0; i--)
            {
                FGradeScoreProfileName profileName = (FGradeScoreProfileName)config.GradeScoreProfileName[i];
                if (sumValue >= profileName.Score)
                {
                    OutProfileName = profileName.ProfileName;
                    break;
                }
            }

            return OutProfileName;
        }

        public string FindMatchingProfile(string GPUFamily, string GLVersion, string AndroidVersion, string DeviceMake,
            string DeviceModel, string VulkanAvailable, string VulkanVersion, string UsingHoudini,
            string MemorySizeInGB, string CPUNumCores, string CPUMaxFreq)
        {
            string OutProfileName = "";
            // 先找白名单
            OutProfileName = FindMatchingProfile(config.GradeMatchProfileWhiteList, GPUFamily, GLVersion,
                AndroidVersion, DeviceMake, DeviceModel, VulkanAvailable, VulkanVersion, UsingHoudini, MemorySizeInGB,
                CPUNumCores, CPUMaxFreq);
            if (!(OutProfileName.Length <= 0))
            {
                return OutProfileName;
            }

            OutProfileName = FindMatchingProfileCommon(GPUFamily, GLVersion, AndroidVersion, DeviceMake, DeviceModel,
                VulkanAvailable, VulkanVersion, UsingHoudini, MemorySizeInGB, CPUNumCores, CPUMaxFreq);
            return OutProfileName;
        }

        // 获取配置文件名 可以根据这个细化到某一个类型的设备
        public string FindMatchingProfile()
        {
            return FindMatchingProfile(GPUFamily, GLVersion, OSVersion, DeviceMake, DeviceModel, VulkanAvailable,
                VulkanVersion, UsingHoudini, MemorySizeInGB, CPUNumCores, CPUMaxFreq);
        }

        // 获取当前设备等级 1 2 3是低配 4 5是中配 6 7是高配
        public ETCQualityGrade GetDeviceGrade()
        {
            string outProfileName = FindMatchingProfile(GPUFamily, GLVersion, OSVersion, DeviceMake, DeviceModel,
                VulkanAvailable, VulkanVersion, UsingHoudini, MemorySizeInGB, CPUNumCores, CPUMaxFreq);
            string gradeStr = ProfileGradeMap[outProfileName];
            //gradeStr = "";
            ETCQualityGrade grade = ETCQualityGrade.Grade01;
            if (Enum.IsDefined(typeof(ETCQualityGrade), gradeStr))
            {
                grade = (ETCQualityGrade)Enum.Parse(typeof(ETCQualityGrade), gradeStr);
            }
            else
            {
                Debug.LogError("ETCQualityGrade config error" + gradeStr);
            }

            return grade;
        }

        // 是否在黑名单里面
        public bool IsInBlackList()
        {
            string OutProfileName = "";
            // 查找
            OutProfileName = FindMatchingProfile(config.GradeMatchProfileBlackList, GPUFamily, GLVersion, OSVersion,
                DeviceMake, DeviceModel, VulkanAvailable, VulkanVersion, UsingHoudini, MemorySizeInGB, CPUNumCores,
                CPUMaxFreq);
            return (OutProfileName != string.Empty);
        }
    }
}